let str = 'good good study, day day up';
let reg = /\d+/; // \d 表示 0-9的数字，+ 表示左边出现1次或多次，该正则匹配的是个位数和多位数，是否包含数字
reg.test(str); // => false，test方法用来判断当前字符串是否和这个规则匹配，返回布尔值

str = '2021-4-6';
reg.exec(str); // exec捕获符合正则的结果 => ['2019',index:0,inputs:'原始字符串2021-4-6']

// 正则表达式
//  创建正则：字面量方式，构造函数方式
//  构造函数创建，两个参数：元字字符串，修饰符字符串

let reg1 = new RegExp('\\d+'); // 第一个\转义字符

/**
 * 元字符：
    * 特殊元字符：单个或者组合在一起代表特殊的含义
    *   \  转义字符
    *   .  除\n(换行符)以外的任意字符，除了换行符.都可以代表
    *   ^  以哪个元字符作为开始，与方括号一起使用，表示除了
    *   $  以哪个元字符作为结束
    *   \n  换行符
    *   \d  0-9之间的一个数字
    *   \D  非0-9之间的一个数字
    *   \w  数字、字母、下划线中的任意一个字符
    *   \s  一个空白字符，包括空格、制表符、换页符等
    *   \t  一个制表符(一个tab键)
    *   \b  匹配一个单词的边界
    *   x|y  x或者y中的一个字符
    *   [xyz]  x或者y或者z中的一个字符
    *   [^xy]  除x或者y意外的任意字符
    *   [a-z]  指定a-z这个范围中的任意字符 [0-9a-zA-Z_]0-9或者a-z或者A-Z或者_任意一个字符，这是四部分，也就是[0-9a-zA-Z_] === \w
    *   ()  正则中的分组符号
    *   (?:)  只匹配不捕获
    *   (?=)  正向预查
    *   (?!)  负向预查
    * 量词元字符：设置出现的次数
    *    *星号：0到多次，
    *    +加号：1到多次，
    *    ?问好：0次或1次，
    *    {n}：出现n次，n取值0或正整数，
    *    {n,}：出现n到多次，
    *    {n,m}：
    *    出现n到m次，包括n和m
    * 普通元字符：代表本身含义的
    *    /zhufeng/，这个正则匹配的就是'zhufeng'
 * 修饰符
 *   i  忽略单词大小写匹配
 *   m  可以进行多行匹配
 *   g  全局匹配
 */
/**
 * 详解
 * 1、^$
 * /^\d+$/  字符串必须完全正则的规则，必须是1或多个正整数
 * /\d/  字符串包含正整数即可
 * 2、转义符 \
 * /^2\.3$/ 把小数点转义，让它代表小数点
 * 3、x|y
 * /^18|29$/，可能会出现歧义，用小括号包起来，/^(18|29)$/，小括号优先级高，先看小括号里边的
 * 4、[]里边的内容一般都代表本身含义
 * /^[@+]$/  匹配的是@字符串或者+号
 * /^[\d]$/ 匹配的是\d，也就是0-9之间的数字
 * /^[\\d]$/ 匹配的是字符串\或者字符串d
 * []里不存在多位数，/^18$/匹配的是1或者8，18不匹配
 * 
 * 例题：
 * 验证是否为有效数字
 * 1,+1,-1,1.2
 * let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
 * 
 * 验证密码
 * 只能是数字、字母、下划线，6-16位
 * let reg = /\w{6-16}/
 * 
 * 验证真实姓名
 * 汉字 u4E00-u9FA5，2到10位，可能有·，尼古拉斯·赵四
 * let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-u9FA5]){0,2}$/
 * 
 * 验证邮箱
 * 分析邮箱正则，推荐用下边这个正则
 * 企业邮箱：zhao-meiyang@163.com.cn
 * let reg = /^\w+((-\w+)|(\.\w+))*@[A-Z0-9a-z]+((\.|-)[A-Za-z0-9]+))*\.[A-Za-z0-9]+$/
 * 
 * 验证身份证号
 * 前六位 省市县，中间8位年月日，最后一位X或者数字，倒数第二位性别，男奇女偶
 * let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/
 * reg.exec('13022119960112765X')能拿到精准信息
 * 
 * 创建正则的两种方式和区别
 * 1、构造函数因为传递的是字符串，\需要写两个才代表斜杠
 * let reg = /\d+/g;
 * reg = new RegExp("\\d+","g");
 * 2、正则表达式中的部分内容是变量存储的值
 * 如果正则中要包含某个变量的值，则不能用字面量的方式创建
 * let type = 'number'
 * reg = /^@"+type+"@$/   这个正则匹配的是@""""typeeeeee"@
 * reg = new RegExp("^@"+type+"@$")   这个正则可以匹配到变量
 * 
 * 正则表达式捕获的特性
 *    正则捕获的前提是当前正则要和字符串匹配，如果不匹配捕获的结果是null
 *    实现正则捕获的办法
 *       正则里边的方法：exec、test，字符串里边的方法：replace、match、slite...
 *    let str = 'zhufeng2019yangfang2020qihang2021';
 *    let reg = /\d+/;
 *    reg.exec(str); // 返回数组或者null，每次执行exec只能捕获到一个结果，就算执行100遍，结果永远都是第一个匹配到的
 *    正则的懒惰性：默认只捕获第一个，原因是lastIndex总是0
 *    reg.lastIndex => 0，lastIndex：当前正则下一次匹配的起始索引位置
 *    执行完exec这个方法以后，会把lastIndex的值修改
 *    全匹配让reg = /\d+/g，然后执行reg.exec(str)
 *    reg.test()方法执行，也会改变lastIndex的值，被修改为第一次匹配后的结果
 *    => 编写一个方法，execAll，执行一次可以捕获到所有匹配的结果
 *       while循环知道结果不知道循环几次
 *    ~function() {
 *       function execAll(str = '') {
 *          // 验证正则表达式是否加g了
 *          if(!this.global) return this.exec(str);
 *          let arr = [],
 *              res = this.exec(str)
 *          while(res) {
 *             arr.push(res[0]);
 *             // 只要捕获的内容不为null，继续捕获
 *             res = this.exec(str);
 *          }
 *          return arr.length == 0 ? null : arr
 *       }
 *       RegExp.prototype.execAll = execAll
 *    }()
 *    第二种方法借助字符串的match方法，正则中也必须设置g，可以获取到所有匹配到的结果
 *    'zhufeng2019yangfang2020qihang2021'.match(/\d+/g);
 * 
 * 正则的分组捕获
 *    let str = '13022119960112765X';
 *    let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
 *    reg.exec(str);
 *    => 结果第一项是大正则匹配的结果，其余项是每个小分组单独匹配捕获的结果
 *    => 如果不想匹配某一项，只验证不捕获，使用问好冒号（?:），例如：(?:\d|X)
 * 
 *    => 既要捕获到{数字}，又要把数字单独捕获
 *    let str = '{0}年{1}月{2}日';
 *    let reg = /\{(\d+)\}/g;
 *    => 上边这个方法只能捕获大正则，小分组内容无法获取到
 *    解决办法：
 *    let aryBig = [];
 *    let arySml = [];
 *    let res = reg.exec(str);
 *    while(res) {
 *       let [big, small] = res;
 *       aryBig.push(big);
 *       arySml.push(small);
 *       res = reg.exec(str);
 *    }
 *    console.log(aryBig, arySml)
 * 分组引用
 *    let str = 'book';
 *    let reg = /[a-zA-Z]([a-zA-Z])\1[a-zA-Z]/;
 *    => \1就是让之前的分组出现一模一样的内容几次，
 * 正则捕获的贪婪性
 * 默认情况下，正则捕获的时候，是按照当前正则所匹配的最长结果来获取的
 *    let reg = /\d+/g;
 *    console.log('zhufeng2019@2020peixun'.match(reg)) => ['2019','2000'],
 *    结果是2019和2020，其实也会捕获到2，0，1，9等等这样的，由于贪婪行捕获的是最长的
 *    let reg = /\d+?/g;
 *    ?让正则取消贪婪性，单个去捕获
 *    结果=> ["2"0"1"9"2"0"2"0"]
 * ?的五种特殊含义：
 *    问好左边是非量词元字符，本身代变量词元字符，出现零到一次
 *    问好左边是量词元字符，取消捕获的贪婪性
 *    ?: 只匹配不捕获
 *    ?= 正向预查
 *    ?! 负向预查
 * 
 * 其他正则捕获的方法
 *    1、test也能捕获
 *    let str = '{0}年{1}月{2}日'
 *    let reg = /\{(\d+)\}/g;
 *    console.log(reg.test(str)) => true
 *    console.log(RegExp.$1); => 0,匹配到的是第一个
 *    console.log(reg.test(str)) => true
 *    console.log(RegExp.$1); => 1,匹配到的是第二个
 *    RegExp.$1~RegExp.$9：获取当前本次正则匹配后，第一个到第九个分组的信息
 *    2、replace字符串中实现替换的方法
 *    let str = 'zhufeng@2019|zhufeng@2020';
 *    正则基于g可以实现
 *    str = str.replace(/zhufeng/g, '珠峰')
 *    let str = '2021-04-18';
 *   => '2021年4月18日'
 *    使用分组：
 *    let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
 *    time = time.replace(reg, '$1年$2月$3日');
 *    使用函数
 *    time.replace(reg, (big, ...[args])=>{
 *       let [$1,$2,$3] = args;
 *       return $1+'年'+$2+'月'+$3+'日'
 *    })
 *    3、单词首字母大写
 *    let str = 'good good study'
 *    let reg = /\b([a-z][A-Z])[a-zA-Z]*\b/g;
 *    str = str.replace(reg, (...arg)=>{
 *       let [content, $1] = [arg];
 *       $1 = $1.toUpperCase();
 *       content = content.substr(1);
 *       return $1 + content
 *    })
 *    4、验证字符串中字母出现次数最多，多少次
 *    let str = 'good good study';
 *   => 去重思维,使用对象和属性
 *   => 分组排序
 *    字母的比较使用localeCompare,a.localeCompare(b)
 *    str = str.split('').sort((a,b) => a.localeCompare(b))
 *    let reg = /([a-zA-Z])\1+/g
 *    let ary = str.match(reg);
 *    5、格式化时间字符串
 *    let str = '2021-4-13 16:51:3'
 *    6、路由参数的获取,?后边的参数，也包含hash值
 *    http://www.baidu.com/?name=zs&age=18#video
 *    function queryParams(url) {
 *       let obj = {};
 *       url.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[,$1,$2])=> obj[$1] = $2 );
 *       url.replace(/#[^?=&#]+/g, (...[,$1] => obj['HASH'] = $1));
 *       return obj
 *    }
 *    7、千分符
 *    function millimeter() {
 *       return this.replace(/\d{1,3}(?=(\d{3})+$)/g, content => content + ',')
 *    }
 *    8、匹配路由中的参数
 *    function query() {
 *      let obj = {};
 *      let str = location.search;
 *      str.replace(/([^?=&])=([^?=&]+)/g, function(a, b, c) {
 *          obj[b] = c;
 *      })
 *      return obj
 *    }
 *    9、比较版本号
 *    function compare(a, b) {
 *       let reg = /^\d+(\.\d+){2}(-(alpha|beta|rc)\.\d+)?$/i, n = -1, diff, flag;
 *       if(!reg.test(a) || !reg.test(b)) throw new TypeError('请输入正确的版本号');
 *       a = a.split(/(?:\.|-)/g);
 *       b = b.split(/?:\.|-/g);
 *       cont recur = () => {
 *          n++;
 *          let item1 = a[n], item2 = b[n];
 *          if(!item1 || !item2) {
 *             flag = (!item1 && !item2) ? 0 : (!item1 ? 1 : -1);
 *             return
 *          }
 *          diff = (isNaN(item1) || isNaN(item2)) ? (item1.localeCompare(item2)) : (item1 - item2);
 *          if(diff === 0) {
 *             recur();
 *             return;
 *          }
 *          flag = diff > 0 ? 1 : -1;
 *       }
 *    };
 *    recur();
 *    return flag;
 *    9、验证表情和其他特殊字符
 *    let reg = /^[\u4E00-\u9FA5\r\n 0-9a-zA-Z,，.。;；:：”"'’!！`·？?￥《》<>｜、\\|~～{「」【】@#¥%……&*（）——}@#$%^&*()-_+=[\]]+$/;
 */